package io.github.uicdb.et.recipe;

import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.github.uicdb.et.ElementalTheory;
import io.github.uicdb.et.ModElements;
import io.github.uicdb.et.capability.IElementalHandler;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.NonNullList;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.*;
import net.minecraft.world.level.Level;
import net.neoforged.neoforge.common.util.RecipeMatcher;
import org.jetbrains.annotations.NotNull;

import java.util.List;

@Getter
@RequiredArgsConstructor
public class ElementRecipe implements Recipe<ElementRecipeInput> {
    public static final RecipeType<ElementRecipe> TYPE = RecipeType.simple(ResourceLocation.fromNamespaceAndPath(ElementalTheory.MODID, "element_recipe"));

    final List<ElementRecipePart> parts;
    final List<Ingredient> items;
    final ItemStack result;
    public static final MapCodec<ElementRecipe> CODEC = RecordCodecBuilder.mapCodec(
            instance -> instance.group(
                    ElementRecipePart.CODEC.listOf().fieldOf("inputs").forGetter(ElementRecipe::getParts),
                    Ingredient.CODEC_NONEMPTY.listOf().fieldOf("items").forGetter(ElementRecipe::getItems),
                    ItemStack.CODEC.fieldOf("result").forGetter(ElementRecipe::getResult)
            ).apply(instance, ElementRecipe::new)
    );
    public static final StreamCodec<RegistryFriendlyByteBuf, ElementRecipe> STREAM_CODEC = StreamCodec.composite(
            ElementRecipePart.STREAM_CODEC.apply(ByteBufCodecs.list()),
            ElementRecipe::getParts,
            Ingredient.CONTENTS_STREAM_CODEC.apply(ByteBufCodecs.list()),
            ElementRecipe::getItems,
            ItemStack.STREAM_CODEC,
            ElementRecipe::getResult,
            ElementRecipe::new
    );

    @Override
    public boolean matches(ElementRecipeInput input, @NotNull Level level) {
        return matchItems(input)
                && matchElement(input.elementalHandler());
    }

    private boolean matchItems(ElementRecipeInput input) {
        var nonEmptyItems = new java.util.ArrayList<ItemStack>(input.getIngredientCount());
        for (var item : input.getItems())
            if (!item.isEmpty())
                nonEmptyItems.add(item);
        return RecipeMatcher.findMatches(nonEmptyItems, items) != null;
    }

    private boolean matchElement(IElementalHandler elementalHandler) {
        for (ElementRecipePart inputElemental : parts) {
            if (elementalHandler.getElemental(ModElements.byHolder(inputElemental.element())) < inputElemental.amount())
                return false;
        }
        return true;
    }

    @Override
    public @NotNull ItemStack assemble(@NotNull ElementRecipeInput elementRecipeInput, HolderLookup.@NotNull Provider provider) {
        return result.copy();
    }

    @Override
    public boolean canCraftInDimensions(int i, int i1) {
        return true;
    }

    @Override
    public ItemStack getResultItem(HolderLookup.Provider provider) {
        return result;
    }

    @Override
    public @NotNull RecipeSerializer<?> getSerializer() {
        return Serializer.INSTANCE;
    }

    @Override
    public @NotNull RecipeType<?> getType() {
        return TYPE;
    }

    public static class Serializer implements RecipeSerializer<ElementRecipe> {
        public static final ElementRecipe.Serializer INSTANCE = new ElementRecipe.Serializer();

        public @NotNull MapCodec<ElementRecipe> codec() {
            return ElementRecipe.CODEC;
        }

        public @NotNull StreamCodec<RegistryFriendlyByteBuf, ElementRecipe> streamCodec() {
            return ElementRecipe.STREAM_CODEC;
        }

    }
}
